package com.zyf.project.binaryproto.common;

import com.zyf.project.binaryproto.annotation.DataContract;
import com.zyf.project.binaryproto.contract.DataContractEncoder;
import com.zyf.project.binaryproto.contract.impl.DataContractContext;
import com.zyf.project.binaryproto.encoder.impl.HeaderEncoder;
import com.zyf.project.binaryproto.exception.DataContractException;
import utils.binary.impl.BytesSlice;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <h1>二进制数据协议</h1>
 *
 * @author Zhou Yifan
 */
public class BinaryProtocol {

    private static final Object DYNAMIC_CONTRACT_TYPE_MUTEX = new Object();

    private static Map<Class<?>, Class<?>> dynamicContractTypeMapping = new ConcurrentHashMap<>();

    BinaryProtocol() {
    }

    /**
     * 对指定数据按照指定类型进行编码
     *
     * @param data         指定数据
     * @param contractType 指定类型
     * @return 字节数组 byte[]
     */
    public static byte[] encode(Object data, Class<?> contractType) {
        DataContractEncoder encoder = DataContractRegistry.register(contractType);
        if (encoder == null) {
            throw new IllegalArgumentException(
                    "Contract Type not exist!--" + contractType.getName()
            );
        }
        return encoder.encode(data);
    }

    /**
     * 对指定的数据进行解码
     *
     * @param dataSegment 指定的数据片段，byte[]
     * @param <T>         返回类型
     * @return 解码后的对象
     */
    public static <T> T decode(byte[] dataSegment) {
        BytesSlice bytes = new BytesSlice(dataSegment, 0, dataSegment.length);
        int code = HeaderEncoder.resolveCode(bytes);
        long version = HeaderEncoder.resolveVersion(bytes);

        DataContractEncoder encoder = DataContractRegistry.getEncoder(code, version);
        if (encoder == null) {
            throw new DataContractException(
                    String.format("No data contract was registered with code[%s] and version[%s]!", code, version));
        }
        return encoder.decode(dataSegment);
    }

    /**
     * 对指定数据按照指定数据契约进行解码
     *
     * @param dataSegment  指定数据
     * @param contractType 指定数据契约类型
     * @param <T>          返回类型
     * @return 解码后的对象
     */
    public static <T> T decode(byte[] dataSegment, Class<T> contractType) {
        return decode(dataSegment, contractType, true);
    }

    /**
     * 对指定数据按照指定数据契约进行解码
     *
     * @param dataSegment  指定数据
     * @param contractType 指定数据契约类型
     * @param autoRegister 是否自动注册
     * @param <T>          返回类型
     * @return 解码后的对象
     */
    public static <T> T decode(byte[] dataSegment, Class<T> contractType, boolean autoRegister) {
        BytesSlice bytes = new BytesSlice(dataSegment, 0, dataSegment.length);
        int code = HeaderEncoder.resolveCode(bytes);
        long version = HeaderEncoder.resolveVersion(bytes);
        DataContractEncoder encoder = DataContractRegistry.getEncoder(code, version);
        if (encoder == null) {
            encoder = DataContractRegistry.getEncoder(contractType);
        } else {
            if (!contractType.isAssignableFrom(encoder.getContractType())) {
                throw new DataContractException("The specified contract type[" + contractType.getName()
                        + "] is incompatible the origin contract type[" + encoder.getContractType().getName()
                        + "] of the data bytes!");
            }
        }
        if (encoder == null) {
            if (autoRegister) {
                encoder = DataContractRegistry.register(contractType);
            } else {
                throw new DataContractException("Contract type is not registered! --" + contractType.toString());
            }
        }
        return encoder.decode(dataSegment);
    }

    /**
     * 判断指定类型是否声明为 {@link DataContract}
     *
     * @param contractType 指定类型
     * @return 布尔值
     */
    public static boolean isDataContractType(Class<?> contractType) {
        return DataContractContext.isDataContractType(contractType);
    }

}
